﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Globalization;
using Medianamik.Core.Interfaces;
using Medianamik.Core.Caching;
using System.Collections;

namespace Medianamik.Core.Querying
{
    public class Query  : IQuery   
    {
        public Query(INodeCacheProvider pCacheProvider, IQueryDefinition pDefinition)
            :this(pCacheProvider ?? new CurrentRequestCacheProvider(), 
            pDefinition ?? new QueryDefinition(), false)
        {
            
        }
        public Query(): this(new CurrentRequestCacheProvider(), new QueryDefinition(), false)
        {
        }
        public Query(INodeCacheProvider pCacheProvider, IQueryDefinition pDefinition, bool withDeletedNodes)
        {
            CacheProvider = pCacheProvider ?? new CurrentRequestCacheProvider();
            Definition = pDefinition ?? new QueryDefinition();
            WithDeletedNodes = withDeletedNodes;
            Definition.LazyLoading = true;
        }
        public Query(bool withDeletedNodes)
            : this(new CurrentRequestCacheProvider(), new QueryDefinition(), withDeletedNodes)
        {
        }

        protected virtual INodeCacheProvider CacheProvider
        {
            get;
            private set;
        }

        internal virtual IQueryDefinition Definition
        {
            get;
            private set;
        }

        protected bool WithDeletedNodes { get; private set; }
        
        public OrderedQuery OrderBy<TResult>(Expression<Func<IQueryableNode, TResult>> pExpression)
        {
            return new OrderedQuery(this).ThenBy(pExpression);
        }

        public OrderedQuery OrderByDesc<TResult>(Expression<Func<IQueryableNode, TResult>> pExpression)
        {
            return new OrderedQuery(this).ThenByDesc(pExpression);
        }
              
        public IEnumerable<Node> Execute()
        {
            return CacheProvider.GetNodes(Definition, WithDeletedNodes);
        }

        public int Count()
        {
            return CacheProvider.GetNodeCount(this.Definition);
        }
 
        public Query<T> AsQuery<T>() where T:INodeWrapper, new()
        {
            return new Query<T>(this);
        }
        
        #region IQuery Members

        IEnumerable<Node> IQuery.Execute()
        {
            return this.Execute();
        }

        INodeCacheProvider IQuery.CacheProvider
        {
            get { return this.CacheProvider; }
        }

        IQueryDefinition IQuery.Definition
        {
            get { return this.Definition; }
        }

        IQuery<T> IQuery.AsQuery<T>() 
        {
            return this.AsQuery<T>();
        }

        #endregion
    }

    public class Query<T> : IQuery<T> where T : INodeWrapper, new()
    {

        public Query(IQuery pInnerQuery)
        {
            InnerQuery = pInnerQuery;
        }

        public IQuery InnerQuery
        {
            get;private set;
        }

        public IEnumerable<T>  Execute()
        {
            return InnerQuery.Execute()
                .Select(n=> new T() { InnerNode = n});
        }

        public int Count()
        {
            return InnerQuery.Count();
        }

        #region IQuery Members

        IEnumerable<Node> IQuery.Execute()
        {
            return InnerQuery.Execute();
        }

        INodeCacheProvider IQuery.CacheProvider
        {
            get { return InnerQuery.CacheProvider; }
        }

        IQueryDefinition IQuery.Definition
        {
            get { return InnerQuery.Definition; }
        }

        IQuery<T> IQuery.AsQuery<T>()
        {
            return InnerQuery.AsQuery<T>() ;
        }

        #endregion
    } 
}
